package visitor.outputvisitor

import ast.expr.SqlIdentifierExpr
import ast.expr.SqlPropertyExpr
import ast.limit.SqlLimit
import ast.statement.select.SqlWithItem
import ast.statement.select.SqlWithSelect
import ast.statement.upsert.SqlUpsert

class OracleOutPutVisitor : SqlOutPutVisitor() {
    override fun visitSqlLimit(sqlLimit: SqlLimit) {
        sqlBuilder.append("OFFSET ${sqlLimit.offset} ROWS FETCH FIRST ${sqlLimit.limit} ROWS ONLY")
    }

    override fun visitSqlWithSelect(sqlWithSelect: SqlWithSelect) {
        sqlBuilder.append("WITH ")

        fun visitSqlWithItem(sqlWithItem: SqlWithItem) {
            visitSqlExpr(sqlWithItem.name)
            if (sqlWithItem.columns.isNotEmpty()) {
                sqlBuilder.append("(")
                printList(sqlWithItem.columns, ::visitSqlExpr)
                sqlBuilder.append(")")
            }
            sqlBuilder.append(" AS ")
            sqlBuilder.append("(")
            visitSqlSelectQuery(sqlWithItem.query)
            sqlBuilder.append(")")
            sqlBuilder.append("\n")
        }

        printList(sqlWithSelect.with, ::visitSqlWithItem)

        visitSqlSelectQuery(sqlWithSelect.query!!)
    }

    override fun visitSqlUpsert(sqlUpsert: SqlUpsert) {
        sqlBuilder.append("MERGE INTO ")
        visitSqlExpr(sqlUpsert.table!!)
        sqlBuilder.append(" ${quote}t1${quote}")

        sqlBuilder.append(" USING (")
        sqlBuilder.append("SELECT ")
        sqlUpsert.columns.forEachIndexed { index, item ->
            visitSqlExpr(sqlUpsert.value[index])
            sqlBuilder.append(" AS ")
            visitSqlExpr(item)
            if (index < sqlUpsert.columns.size - 1) {
                sqlBuilder.append(",")
                sqlBuilder.append(" ")
            }
        }
        sqlBuilder.append(" FROM ${quote}dual${quote}) ${quote}t2${quote}")

        sqlBuilder.append(" ON (")
        sqlUpsert.primaryColumns.forEachIndexed { index, item ->
            sqlBuilder.append("${quote}t1${quote}.")
            visitSqlExpr(item)
            sqlBuilder.append(" = ")
            sqlBuilder.append("${quote}t2${quote}.")
            visitSqlExpr(item)
            if (index < sqlUpsert.primaryColumns.size - 1) {
                sqlBuilder.append(" AND ")
            }
        }
        sqlBuilder.append(")")

        sqlBuilder.append(" WHEN MATCHED THEN UPDATE SET ")
        printList(sqlUpsert.updateColumns) {
            sqlBuilder.append("${quote}t1${quote}.")
            visitSqlExpr(it)
            sqlBuilder.append(" = ")
            sqlBuilder.append("${quote}t2${quote}.")
            visitSqlExpr(it)
        }

        sqlBuilder.append(" WHEN NOT MATCHED THEN INSERT")

        sqlBuilder.append(" (")
        printList(sqlUpsert.columns) {
            sqlBuilder.append("${quote}t1${quote}.")
            visitSqlExpr(it)
        }
        sqlBuilder.append(")")

        sqlBuilder.append(" VALUES")
        sqlBuilder.append(" (")
        printList(sqlUpsert.value, ::visitSqlExpr)
        sqlBuilder.append(")")
    }
}